package com.sinszm.web;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.gson.Gson;
import com.sinszm.web.annotation.Swagger;
import com.sinszm.web.annotation.Ws;
import com.sinszm.web.exception.GlobalErrorPageInterceptor;
import com.sinszm.web.interceptor.MRequestInterceptor;
import com.sinszm.web.properties.SwaggerProperties;
import com.sinszm.web.properties.WebProperties;
import com.sinszm.web.properties.WebSocketProperties;
import com.sinszm.web.request.MRequestFilter;
import com.sinszm.web.socket.MWebSocketHandler;
import com.sinszm.web.socket.MWebSocketInterceptor;
import com.sinszm.web.support.DocPrediate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.*;

import static java.util.stream.Collectors.toList;

/**
 * SzmWeb自动配置
 *
 * @author chenjianbo
 */
@ServletComponentScan(basePackageClasses = {MRequestFilter.class})
@EnableConfigurationProperties({WebSocketProperties.class, WebProperties.class, SwaggerProperties.class})
public class SzmWebAutoConfiguration implements WebMvcConfigurer {

    private final Gson gson;

    private final WebProperties webProperties;

    private static final String[] EXCLUDE_URI = {
            "/favicon.ico",
            "/error",
            "/doc.html",
            "/webjars/**",
            "/swagger-resources/**",
            "/thinkswagger/**"
    };

    @Autowired
    public SzmWebAutoConfiguration(
            Gson gson,
            WebProperties webProperties) {
        this.gson = gson;
        this.webProperties = webProperties;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //请求日志拦截
        List<String> patterns = Optional.ofNullable(webProperties.getPatterns()).orElse(new ArrayList<>());
        if (patterns.isEmpty()) {
            patterns.add("/**");
        }
        List<String> ePatterns = Optional.ofNullable(webProperties.getExcludePatterns()).orElse(new ArrayList<>());
        ePatterns.addAll(Arrays.asList(EXCLUDE_URI));
        registry.addInterceptor(new MRequestInterceptor(gson)).addPathPatterns(patterns).excludePathPatterns(ePatterns);
        //系统错误异常日志拦截
        registry.addInterceptor(new GlobalErrorPageInterceptor());
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/thinkswagger/**").addResourceLocations("/WEB-INF/thinkswagger/");
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }

    /**
     * WebSocket配置
     */
    @Ws
    @EnableWebSocket
    public static class SzmWebSocketConfiguration implements WebSocketConfigurer {

        private final WebSocketProperties properties;
        private final ApplicationContext context;

        @Autowired
        public SzmWebSocketConfiguration(WebSocketProperties properties, ApplicationContext context) {
            this.properties = properties;
            this.context = context;
        }

        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            List<String> uri = properties.getUri();
            registry.addHandler(
                    new MWebSocketHandler(context, properties),
                    uri.toArray(new String[0])
            ).setAllowedOrigins("*")
                    .addInterceptors(new MWebSocketInterceptor());
            //添加对SockJs的支持
            List<String> jsUri = properties.getJsUri();
            if (jsUri != null && jsUri.size() > 0) {
                registry.addHandler(
                        new MWebSocketHandler(context, properties),
                        jsUri.toArray(new String[0])
                ).setAllowedOrigins("*")
                        .addInterceptors(new MWebSocketInterceptor()).withSockJS();
            }
        }

    }

    /**
     * 接口UI配置
     */
    @Swagger
    @EnableSwagger2
    @EnableSwaggerBootstrapUI
    public static class SwaggerConfiguration {

        private final SwaggerProperties swaggerProperties;

        @Autowired
        public SwaggerConfiguration(SwaggerProperties swaggerProperties) {
            this.swaggerProperties = swaggerProperties;
        }

        /**
         * 接口文档配置
         * @return  ui实例
         */
        @Bean
        public Docket swaggerUi() {
            ApiSelectorBuilder asb = new Docket(DocumentationType.SWAGGER_2)
                    .groupName("Document")
                    .pathMapping(swaggerProperties.swaggerBasePath())
                    .apiInfo(
                            new ApiInfoBuilder()
                                    .title(StrUtil.trimToEmpty(swaggerProperties.getTitle()))
                                    .version(StrUtil.trimToEmpty(swaggerProperties.getVersion()))
                                    .description(StrUtil.trimToEmpty(swaggerProperties.getDescription()))
                                    .contact(swaggerProperties.getContact())
                                    .termsOfServiceUrl(swaggerProperties.getContact().getUrl())
                                    .licenseUrl(swaggerProperties.getLicenseUrl())
                                    .build()
                    )
                    .select();
            //取得多个包路径设置
            if (!ObjectUtil.isEmpty(swaggerProperties.getPackages())) {
                asb.apis(Predicates.or(
                        swaggerProperties.getPackages()
                                .stream()
                                .map(RequestHandlerSelectors::basePackage)
                                .map(pre -> (Predicate<RequestHandler>) new DocPrediate<>(pre))
                                .collect(toList())
                ));
            }
            return asb.paths(PathSelectors.any()).build();
        }
    }

}
